// WinnerCache.cpp: implementation of the CWinnerCache class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#pragma warning( disable : 4786 )
#include "WinnerCache.h"

#include <osmem.h>
#include <log.h>

#include "Configuration.h"

//////////////////////////////////////////////////////////////////////
// Interface
//////////////////////////////////////////////////////////////////////

void CWinnerCache::Cache(const string& sUsername, const string& sPassword)
{
	if (Running != m_stateCleanerProc)
	{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::Cache(): EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		EnterCriticalSection( &m_csObj_mapWinners );
		if (m_mapWinners.size() > 5000)
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::Cache(): manually clean up."), NULLHANDLE, NOERROR, FILTER_NAME );
			m_mapWinners.erase( m_mapWinners.begin() );
		}
		LeaveCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::Cache(): LeaveCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	}

	if (false == _Add( sUsername, sPassword ))
	{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::Cache(): return false(false == _Add())"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		return;
	}

	if (0 == m_hCleanerThread)
	{
		unsigned int threadID= 0;
		m_hCleanerThread = _beginthreadex( NULL, 0, _WinnerCacheCleanerProc, (LPVOID)this, NULL, &threadID );
		if (CConfiguration::IsDebugModule())
			LogEventText( _T("%s(Debug): CWinnerCache::Cache(): threadOfAuthCache: created."), NULLHANDLE, NOERROR, FILTER_NAME );
	}

#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::Cache(): return successfully"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
}

bool CWinnerCache::IsCached(const string& sUserName, const string& sPassword, bool& ifPasswordRight)
{
	bool ifCached= false;
	ifPasswordRight = false;
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::IsCached(): EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	EnterCriticalSection( &m_csObj_mapWinners );
	std::map<string,MEMHANDLE>::const_iterator itUserMatched= m_mapWinners.find( sUserName );
	if (itUserMatched != m_mapWinners.end())
	{
		ifCached = true;

		const MEMHANDLE& hCacheItem= itUserMatched->second;
		if (NULLMEMHANDLE != hCacheItem)
		{
			SCacheItem* pCacheItem= static_cast<SCacheItem*>(OSMemoryLock( hCacheItem ));
			if ((NULL != pCacheItem)
			 && (NULLMEMHANDLE != pCacheItem->hPassword))
			{
				char* pszPassword= static_cast<char*>(OSMemoryLock( pCacheItem->hPassword ));
				if (NULL != pszPassword)
				{
					if (pszPassword == sPassword)
					{
						ifPasswordRight = true;
						pCacheItem->nLeftTime = WINNER_LIVE_TIME;
					}
					else
					{
						ifPasswordRight = false;
					}
				}
				OSMemoryUnlock( pCacheItem->hPassword );
			}
			OSMemoryUnlock( hCacheItem );
		}
	}
	LeaveCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::IsCached(): LeaveCriticalSection( &csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif

	return( ifCached );
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

const unsigned int CWinnerCache::WINNER_LIVE_TIME= 3;
map<string, MEMHANDLE> CWinnerCache::m_mapWinners;

CWinnerCache::CWinnerCache()
	: m_hCleanerThread(0)
	, m_stateCleanerProc(NoThread)
{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache(): call"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif

InitializeCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache(): InitializeCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
}

CWinnerCache::~CWinnerCache()
{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): ~CWinnerCache(): call"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif

#ifdef _DEBUG
LogEventText( _T("%s(Debug): ~CWinnerCache(): EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	EnterCriticalSection( &m_csObj_mapWinners );
	for (std::map<string,MEMHANDLE>::iterator itWinner= m_mapWinners.begin(); itWinner != m_mapWinners.end(); )
	{
		_Remove( itWinner );
	}
	LeaveCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): ~CWinnerCache(): LeaveCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif

	if (0 != m_hCleanerThread)
	{
		_endthreadex( m_hCleanerThread );
		if (CConfiguration::IsDebugModule())
			LogEventText( _T("%s(Debug): ~CWinnerCache(): threadOfAuthCache: terminated"), NULLHANDLE, NOERROR, FILTER_NAME );
	}

	DeleteCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): ~CWinnerCache(): DeleteCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
}

//////////////////////////////////////////////////////////////////////
// Implement
//////////////////////////////////////////////////////////////////////

bool CWinnerCache::_Add(const string& sUsername, const string& sPassword)
{
	if (CConfiguration::IsDebugModule())
		LogEventText( _T("%s(Debug): CWinnerCache::_Add(): '%s'"), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
	
	MEMHANDLE hCacheItem= NULLMEMHANDLE;
		STATUS result= OSMemoryAllocate( 0, sizeof(SCacheItem), &hCacheItem );
		if (NOERROR != result)
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_Add(): '%s' is not added with failed cache item memory allocation."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(failed cache item memory allocation)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
			return( false );
		}
	SCacheItem *pCacheItem= static_cast<SCacheItem*>(OSMemoryLock( hCacheItem ));
		if (NULL == pCacheItem)
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_Add(): '%s' is not added with failed cache item memory lock."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(failed cache item memory lock)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
			OSMemoryFree( hCacheItem );
			return( false );
		}
		ZeroMemory( pCacheItem, sizeof(SCacheItem) );

	MEMHANDLE hPassword= NULLHANDLE;
		size_t cbPwdMem= sPassword.size() + 1;
			if (cbPwdMem > MAXONESEGSIZE)
				cbPwdMem = MAXONESEGSIZE;
		result = OSMemoryAllocate( 0, cbPwdMem, &hPassword );
		if (NOERROR != result)
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_Add(): '%s' is not added with failed password memory allocation."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(failed password memory allocation)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
			OSMemoryFree( hCacheItem );
			return( false );
		}
	char* pszPassword= static_cast<char*>(OSMemoryLock( hPassword ));
		if (NULL == pszPassword)
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_Add(): '%s' is not added with failed password memory lock."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(failed password memory lock)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
			OSMemoryFree( hCacheItem );
			OSMemoryFree( hCacheItem );
			return( false );
		}
		ZeroMemory( pszPassword, cbPwdMem );
		memcpy( pszPassword, sPassword.c_str(), cbPwdMem );
		pszPassword[cbPwdMem-1] = '\0';
		if (FALSE == OSMemoryUnlock( hPassword ))
		{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(password memory is already unlocked)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
			OSMemoryFree( hPassword );
			OSMemoryFree( hCacheItem );
			return( false );
		}
	pCacheItem->hPassword = hPassword;
	pCacheItem->nLeftTime = WINNER_LIVE_TIME;
	if (FALSE == OSMemoryUnlock( hCacheItem ))
	{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return false(cache item memory is already unlocked)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		OSMemoryFree( hPassword );
		OSMemoryFree( hCacheItem );
		return( false );
	}

#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		EnterCriticalSection( &m_csObj_mapWinners );
		m_mapWinners.insert( std::make_pair( sUsername, hCacheItem ) );
		LeaveCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): LeaveCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif

#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Add(): return true"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	return( true );
}

void CWinnerCache::_Remove(std::map<string,MEMHANDLE>::iterator& itWinner)
{
	if (CConfiguration::IsDebugModule())
		LogEventText( _T("%s(Debug): CWinnerCache::_Remove(): call"), NULLHANDLE, NOERROR, FILTER_NAME );

#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Remove(): EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	EnterCriticalSection( &m_csObj_mapWinners );
	MEMHANDLE& hCacheItem= itWinner->second;
	if (NULLHANDLE != hCacheItem)
	{
		SCacheItem* pCacheItem= static_cast<SCacheItem*>(OSMemoryLock( hCacheItem ));
		if (NULL != pCacheItem)
		{
			if (NULLMEMHANDLE != pCacheItem->hPassword)
			{
				OSMemoryFree( pCacheItem->hPassword );
				pCacheItem->hPassword = NULLMEMHANDLE;
			}
		}
		OSMemoryUnlock( hCacheItem );
		OSMemoryFree( hCacheItem );
		hCacheItem = NULLMEMHANDLE;
	}
	if (CConfiguration::IsDebugModule())
	{
		const std::string& sUsername= itWinner->first;
		LogEventText( _T("%s(Debug): CWinnerCache::_Remove(): remove '%s'."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
	}
	itWinner = m_mapWinners.erase( itWinner );
	LeaveCriticalSection( &m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_Remove(): LeaveCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
}

unsigned int __stdcall CWinnerCache::_WinnerCacheCleanerProc(LPVOID lparam)
{
	if (CConfiguration::IsDebugModule())
		LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): call"), NULLHANDLE, NOERROR, FILTER_NAME );
	
	CWinnerCache* pthis= static_cast<CWinnerCache*>(lparam);
	if (NULL == pthis)
	{
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): return false(NULL == pthis)"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		return( -1 );
	}

	/* Initialize Domino thread - required by threads calling the C API. */
    STATUS error= NotesInitThread();
    if (error)
    {
		if (CConfiguration::IsDebugModule())
			LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: error initializing thread."), NULLHANDLE, NOERROR, FILTER_NAME );
		_endthread();
    }
	pthis->m_stateCleanerProc = Running;

	while(Running == pthis->m_stateCleanerProc)
	{
		if (CConfiguration::IsDebugModule())
			LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: pause cache clean processing."), NULLHANDLE, NOERROR, FILTER_NAME );
		Sleep( 1000 );

		if (CConfiguration::IsDebugModule())
			LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: start cache clean processing."), NULLHANDLE, NOERROR, FILTER_NAME );

#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: EnterCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
		EnterCriticalSection( &pthis->m_csObj_mapWinners );
		for (std::map<string,MEMHANDLE>::iterator itWinner= pthis->m_mapWinners.begin(); itWinner != pthis->m_mapWinners.end(); )
		{
			const std::string& sUsername= itWinner->first;
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: clean up '%s'."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );

			if (Terminating == pthis->m_stateCleanerProc)
			{
				if (CConfiguration::IsDebugModule())
					LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: remove '%s'."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
				pthis->_Remove( itWinner );
				continue;
			}
			else
			{
				MEMHANDLE& hCacheItem= itWinner->second;
				if (NULLMEMHANDLE == hCacheItem)
				{
					if (CConfiguration::IsDebugModule())
						LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: remove '%s' with empty data."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
					pthis->_Remove( itWinner );
					continue;
				}
				else
				{
					SCacheItem* pCacheItem= static_cast<SCacheItem*>(OSMemoryLock( hCacheItem ));
					if (NULL != pCacheItem)
					{
						unsigned int &nWinnerLeftTime= pCacheItem->nLeftTime;
						nWinnerLeftTime--;
						if (nWinnerLeftTime <= 0)
						{
							if (CConfiguration::IsDebugModule())
								LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: '%s' is dead."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str() );
							pthis->_Remove( itWinner );
							continue;
						}
						else
						{
							if (CConfiguration::IsDebugModule())
								LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: '%s' left %u seconds."), NULLHANDLE, NOERROR, FILTER_NAME, sUsername.c_str(), nWinnerLeftTime );
						}// if (nWinnerLeftTime <= 0)
					}
					OSMemoryUnlock( hCacheItem );
				}// if (NULL == pItem)
			}// if (Terminating == stateCleanerProc)
			itWinner++;
		}
		if (true == pthis->m_mapWinners.empty())
		{
			if (CConfiguration::IsDebugModule())
				LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: AuthCache: empty."), NULLHANDLE, NOERROR, FILTER_NAME );
			pthis->m_stateCleanerProc = Terminating;
		}
		LeaveCriticalSection( &pthis->m_csObj_mapWinners );
#ifdef _DEBUG
LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: LeaveCriticalSection( &m_csObj_mapWinners )"), NULLHANDLE, NOERROR, FILTER_NAME );
#endif
	}

	NotesTermThread();
	pthis->m_hCleanerThread = 0;
	if (CConfiguration::IsDebugModule())
		LogEventText( _T("%s(Debug): CWinnerCache::_WinnerCacheCleanerProc(): threadOfAuthCache: terminated."), NULLHANDLE, NOERROR, FILTER_NAME );
	_endthread();
	return( 0 );
}
